package com.zwr.pattern.sequence;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

//线程 1 输出 a 5 次，线程 2 输出 b 5 次，
//线程 3 输出 c 5 次。现在要求输出 abcabcabcabcabc 怎么实现
public class ReplaceSequence {

    //    //测试lock版本
//    public static void main(String[] args) throws InterruptedException {
//        LockDemo as = new LockDemo(5);
//        Condition aWaitSet = as.newCondition();
//        Condition bWaitSet = as.newCondition();
//        Condition cWaitSet = as.newCondition();
//        new Thread(() -> {
//            as.print("a", aWaitSet, bWaitSet);
//        }).start();
//        new Thread(() -> {
//            as.print("b", bWaitSet, cWaitSet);
//        }).start();
//        new Thread(() -> {
//            as.print("c", cWaitSet, aWaitSet);
//        }).start();
//
//        as.start(aWaitSet);
//    }
    //测试park()
    public static void main(String[] args) {
        ParkDemo parkDemo = new ParkDemo(5);
        Thread t1 = new Thread(()->{
            parkDemo.print("a",parkDemo.getThreadList().get(1));
        });
        Thread t2 = new Thread(()->{
            parkDemo.print("b",parkDemo.getThreadList().get(2));
        });
        Thread t3 = new Thread(()->{
            parkDemo.print("c",parkDemo.getThreadList().get(0));
        });

        parkDemo.addToList(t1);
        parkDemo.addToList(t2);
        parkDemo.addToList(t3);
        parkDemo.start();
    }
}

/**
 * wait notify
 */
class WaitNotifyDemo {

    private int loopNum;

    private int flag;

    public WaitNotifyDemo(int loopNum, int flag) {
        this.loopNum = loopNum;
        this.flag = flag;
    }

    public void printStr(int currentFlag, int nextFlag, String str) {
        for (int i = 0; i < this.loopNum; i++) {
            synchronized (this) {

                if (this.flag != currentFlag) {
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.print(str);
                flag = nextFlag;
                this.notifyAll();
            }
        }
    }


    public static void main(String[] args) {
        WaitNotifyDemo waitNotifyDemo = new WaitNotifyDemo(5, 1);
        Thread t1 = new Thread(() -> {
            waitNotifyDemo.printStr(1, 2, "a");
        });
        Thread t2 = new Thread(() -> {
            waitNotifyDemo.printStr(2, 3, "b");
        });
        Thread t3 = new Thread(() -> {
            waitNotifyDemo.printStr(3, 1, "c");
        });
        t1.start();
        t2.start();
        t3.start();
    }

}

/**
 * lock版本
 */
class LockDemo extends ReentrantLock {

    private int loopNum;

    public LockDemo(int loopNum) {
        this.loopNum = loopNum;
    }

    public void start(Condition first) {
        this.lock();
        try {
            System.out.println("start...");
            first.signal();
        } finally {
            this.unlock();
        }
    }

    public void print(String str, Condition current, Condition next) {
        for (int i = 0; i < loopNum; i++) {
            this.lock();
            try {
                current.await();
                System.out.print(str);
                next.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                this.unlock();
            }
        }
    }
}

/**
 * park版本
 */
class ParkDemo {
    private int loopNumber;

    private List<Thread> threadList = new ArrayList<>();

    public void addToList(Thread thread) {
        threadList.add(thread);
    }

    public List<Thread> getThreadList() {
        return threadList;
    }

    public ParkDemo(int loopNumber) {
        this.loopNumber = loopNumber;
    }

    public void print(String str, Thread nextThead) {
        for (int i = 0; i < 5; i++) {
            LockSupport.park();
            System.out.println(str);
            LockSupport.unpark(nextThead);
        }

    }

    public void start() {
        for (Thread thread : threadList) {
            thread.start();
        }
        LockSupport.unpark(threadList.get(0));

    }
}
